bitkeeper revision 1.741 (403a3ee3r3VblO5vtoFGItOp9rvEpg)
authormwilli2@equilibrium.research.intel-research.net <mwilli2@equilibrium.research.intel-research.net>
Mon, 23 Feb 2004 17:56:51 +0000 (17:56 +0000)
committermwilli2@equilibrium.research.intel-research.net <mwilli2@equilibrium.research.intel-research.net>
Mon, 23 Feb 2004 17:56:51 +0000 (17:56 +0000)
Add Xen trace buffer and supporting tools.

12 files changed:
.rootkeys
tools/Makefile
tools/xentrace/Makefile [new file with mode: 0644]
tools/xentrace/xentrace.c [new file with mode: 0644]
tools/xentrace/xentrace_cpusplit.py [new file with mode: 0644]
tools/xentrace/xentrace_format.py [new file with mode: 0644]
xen/arch/i386/setup.c
xen/common/dom0_ops.c
xen/common/trace.c [new file with mode: 0644]
xen/include/hypervisor-ifs/dom0_ops.h
xen/include/xeno/config.h
xen/include/xeno/trace.h [new file with mode: 0644]

index 15ed120cc5162f7fe2cab6413ce3c62bc28e2dd1..cd0b234a42cd3297908714da23e778c31a99dad9 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3fbd0a42l40lM0IICw2jXbQBVZSdZg tools/xc/py/Xc.c
 3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xc/py/XenoUtil.py
 3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
+403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
+403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
+403a3edb0lzD0Fojc-NYNoXr3SYrnA tools/xentrace/xentrace_cpusplit.py
+403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format.py
 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING
 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile
 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk
 3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen/common/softirq.c
 3e7f358awXBC3Vw-wFRwPw18qL1khg xen/common/string.c
 3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen/common/timer.c
+403a3edbejm33XLTGMuinKEwQBrOIg xen/common/trace.c
 3ddb79bd3zgV33PHdt-cgh3sxcb1hw xen/common/vsprintf.c
 3ddb79c0ppNeJtjC4va8j41ADCnchA xen/drivers/Makefile
 3ddb79beWzgPS8ozf2BL2g3ZkiWhhQ xen/drivers/block/Makefile
 3ddb79c2HFkXuRxi1CriJtSFmY6Ybw xen/include/xeno/timer.h
 3ddb79c2_m8lT9jDKse_tePj7zcnNQ xen/include/xeno/timex.h
 3ddb79c2e2C14HkndNEJlYwXaPrF5A xen/include/xeno/tqueue.h
+403a3edbG9K5uZjuY19_LORbQGmFbA xen/include/xeno/trace.h
 3ddb79c1-kVvF8cVa0k3ZHDdBMj01Q xen/include/xeno/types.h
 3f055a3dwldYR102YcSuBaxIf9t3Jw xen/include/xeno/vbd.h
 3e8827bdaqPeZAWGVOwswgY9bWSx4g xen/include/xeno/version.h
index cf8b9fc0fa97705edf217b7f7e2434b5b6f6eb28..4717987d6cd8ee99431d424b94f6ee91c0de18ad 100644 (file)
@@ -22,18 +22,21 @@ all:
        $(MAKE) -C xc
        $(MAKE) -C misc
        $(MAKE) -C examples
+       $(MAKE) -C xentrace
 
 install: all
        $(MAKE) -C balloon install
        $(MAKE) -C xc install
        $(MAKE) -C misc install
        $(MAKE) -C examples install
+       $(MAKE) -C xentrace install
 
 dist: all
        $(MAKE) -C balloon dist
        $(MAKE) -C xc dist
        $(MAKE) -C misc dist
        $(MAKE) -C examples dist
+       $(MAKE) -C xentrace dist
 
 clean:
        $(MAKE) -C balloon clean
@@ -41,4 +44,5 @@ clean:
        $(MAKE) -C misc clean
        $(MAKE) -C examples clean
        $(MAKE) -C nsplitd clean
+       $(MAKE) -C xentrace clean
 
diff --git a/tools/xentrace/Makefile b/tools/xentrace/Makefile
new file mode 100644 (file)
index 0000000..52574e8
--- /dev/null
@@ -0,0 +1,32 @@
+
+CC       = gcc
+CFLAGS   = -Wall -O3 
+CFLAGS  += -I../../xen/include -I../../xenolinux-sparse/include
+
+HDRS     = $(wildcard *.h)
+OBJS     = $(patsubst %.c,%.o,$(wildcard *.c))
+
+BIN      = xentrace
+SCRIPTS  = $(wildcard *.py)
+
+all: $(BIN)
+
+install: all
+       mkdir -p /usr/bin
+       cp $(BIN) /usr/bin
+       cp $(SCRIPTS) /usr/bin
+       chmod 755 /usr/bin/$(BIN)
+       for i in $(SCRIPTS); do chmod 755 /usr/bin/$$i; done
+
+dist: all
+       mkdir -p ../../../install/bin
+       cp $(BIN) ../../../install/bin
+       chmod 755 ../../../install/bin/$(BIN)
+       cp $(SCRIPTS) ../../../install/bin
+       for i in $(SCRIPTS); do chmod 755 ../../../install/bin/$i; done
+
+clean:
+       $(RM) *.a *.so *.o *.rpm $(BIN)
+
+%: %.c $(HDRS) Makefile
+       $(CC) $(CFLAGS) -o $@ $< -lxc
diff --git a/tools/xentrace/xentrace.c b/tools/xentrace/xentrace.c
new file mode 100644 (file)
index 0000000..9da21d6
--- /dev/null
@@ -0,0 +1,428 @@
+/******************************************************************************
+ *
+ * tools/xentrace/xentrace.c
+ *
+ * Tool for collecting trace buffer data from Xen.
+ *
+ * Copyright (C) 2004 by Intel Research Cambridge
+ *
+ * Author: Mark Williamson, mark.a.williamson@intel.com
+ * Date:   February 2004
+ *
+ *****************************************************************************/
+
+#include <time.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <argp.h>
+#include <signal.h>
+
+#include "../xc/lib/xc_private.h"
+
+#define TRACE_BUFFER    /* need to define this for trace.h */
+#include <xeno/trace.h>
+
+extern FILE *stdout;
+
+/***** Compile time configuration of defaults ********************************/
+
+#define NUM_CPUS 1 /* XXX this ought to be removed and replaced with something
+                    * cleverer to dynamically query the machine - I'll use a
+                    * dom0 op once I've implemented it! :-) */
+
+/* when we've got more records than this waiting, we log it to the output */
+#define NEW_DATA_THRESH 1
+
+/* sleep for this long (milliseconds) between checking the trace buffers */
+#define POLL_SLEEP_MILLIS 100
+
+/***** The code **************************************************************/
+
+typedef struct settings_st {
+    char *outfile;
+    unsigned int num_cpus;
+    struct timespec poll_sleep;
+    unsigned long new_data_thresh;
+} settings_t;
+
+settings_t opts;
+
+int interrupted = 0; /* gets set if we get a SIGHUP */
+
+void close_handler(int signal)
+{
+    interrupted = 1;
+    fprintf(stderr,"Received signal %d, now exiting\n", signal);
+}
+
+/**
+ * millis_to_timespec - convert a time in milliseconds to a struct timespec
+ * @millis:             time interval in milliseconds
+ */
+struct timespec millis_to_timespec(unsigned long millis)
+{
+    struct timespec spec;
+    
+    spec.tv_sec = millis / 1000;
+    spec.tv_nsec = (millis % 1000) * 1000;
+
+    return spec;
+}
+
+
+/**
+ * print_rec - plain print an event given a pointer to its start
+ * @cpu:       CPU the data came from
+ * @data:      pointer to the start of the event data
+ * @out:       file stream to print out to
+ *
+ * Takes a pointer to a record and prints out the data.
+ */
+void print_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
+{    
+    fprintf(out, "%u %llu %lu %lu %lu %lu %lu %lu\n",
+           cpu, rec->cycles, rec->event, rec->d1, rec->d2,
+           rec->d3, rec->d4, rec->d5);
+}
+
+
+/**
+ * get_tbuf_ptrs - get pointer to trace buffers
+ *
+ * Does a dom0 op to fetch a "pointer" to the trace buffers.  The pointer can't
+ * be dereferenced immediately, since it is a physical address of memory in Xen
+ * space - they are used in this program to mmap the right area from /dev/mem.
+ */
+struct t_buf *get_tbuf_ptrs()
+{
+    int ret;
+    dom0_op_t op;                        /* dom0 op we'll build             */
+    int xc_handle = xc_interface_open(); /* for accessing control interface */
+
+    op.cmd = DOM0_GETTBUFS;
+    op.interface_version = DOM0_INTERFACE_VERSION;
+
+    ret = do_dom0_op(xc_handle, &op);
+
+    xc_interface_close(xc_handle);
+
+    if(ret) {
+        PERROR("Failure to get trace buffer pointer from Xen");
+        exit(EXIT_FAILURE);
+    }
+    
+    return op.u.gettbufs.phys_addr;
+}
+
+/**
+ * map_tbufs - memory map Xen trace buffers into user space
+ * @tbufs:     physical address of the trace buffers
+ *
+ * Given the physical address of the Xen trace buffers, maps them into process
+ * address space by memory mapping /dev/mem.  Returns a pointer to the location
+ * the buffers have been mapped to.
+ */
+struct t_buf *map_tbufs(struct t_buf *tbufs_phys)
+{
+    int dm_fd;                    /* file descriptor for /dev/mem */
+    struct t_buf *tbufs_mapped;
+
+    dm_fd = open("/dev/mem", O_RDONLY);
+
+    if(dm_fd < 0) {
+        PERROR("Open /dev/mem when mapping trace buffers\n");
+        exit(EXIT_FAILURE);
+    }
+    
+    tbufs_mapped = (struct t_buf *)mmap(NULL, opts.num_cpus * TB_SIZE,
+                                        PROT_READ, MAP_SHARED,
+                                        dm_fd, (off_t)tbufs_phys);
+
+    close(dm_fd);
+
+    if(tbufs_mapped == MAP_FAILED) {
+        PERROR("Failed to mmap trace buffers");
+        exit(EXIT_FAILURE);
+    }
+    
+    return tbufs_mapped;
+}
+
+
+/**
+ * init_bufs_ptrs - initialises an array of pointers to the trace buffers
+ * @bufs_mapped:    the userspace address where the trace buffers are mapped
+ *
+ * Initialises an array of pointers to individual trace buffers within the
+ * mapped region containing all trace buffers.
+ */
+struct t_buf **init_bufs_ptrs(void *bufs_mapped)
+{
+    int i;
+    struct t_buf **user_ptrs;
+
+    user_ptrs = (struct t_buf **)calloc(opts.num_cpus, sizeof(struct t_buf *));
+
+    if(!user_ptrs) {
+        PERROR( "Failed to allocate memory for buffer pointers\n");
+        exit(EXIT_FAILURE);
+    }
+    
+    /* initialise pointers to the trace buffers - given the size of a trace
+     * buffer and the value of bufs_maped, we can easily calculate these */
+    for(i = 0; i<opts.num_cpus; i++)
+        user_ptrs[i] = (struct t_buf *)(
+            (unsigned long)bufs_mapped + TB_SIZE * i);
+
+    return user_ptrs;
+}
+
+
+/**
+ * init_rec_ptrs - initialises data area pointers to locations in user space
+ * @tbufs_phys:    physical base address of the trace buffer area
+ * @tbufs_mapped:  user virtual address of base of trace buffer area
+ * @meta:          array of user-space pointers to struct t_buf's of metadata
+ *
+ * Initialises data area pointers to the locations that data areas have been
+ * mapped in user space.  Note that the trace buffer metadata contains physical
+ * pointers - the array returned allows more convenient access to them.
+ */
+struct t_rec **init_rec_ptrs(struct t_buf *tbufs_phys,
+                             struct t_buf *tbufs_mapped,
+                             struct t_buf **meta)
+{
+    int i;
+    struct t_rec **data;
+    
+    data = calloc(opts.num_cpus, sizeof(struct t_rec *));
+    
+    if(!data) {
+        PERROR( "Failed to allocate memory for data pointers\n");
+        exit(EXIT_FAILURE);
+    }
+
+    for(i = 0; i<opts.num_cpus; i++) {
+        data[i] = (struct t_rec *)(
+            (unsigned long)meta[i]->data
+            - (unsigned long)tbufs_phys
+            + (unsigned long)tbufs_mapped
+            );
+    }
+
+    return data;
+}
+
+/**
+ * init_tail_idxs - initialise an array of tail indexes
+ * @bufs:           array of pointers to trace buffer metadata in struct t_buf's
+ *
+ * The tail indexes indicate where we're read to so far in the data array of a
+ * trace buffer.  Each entry in this table corresponds to the tail index for a
+ * particular trace buffer.
+ */
+int *init_tail_idxs(struct t_buf **bufs)
+{
+    int i;
+    int *tails = calloc(opts.num_cpus, sizeof(unsigned int));
+    if(!tails) {
+        PERROR("Failed to allocate memory for tail pointers\n");
+        exit(EXIT_FAILURE);
+    }
+    
+    for(i = 0; i<opts.num_cpus; i++)
+        tails[i] = bufs[i]->head;
+
+    return tails;
+}
+
+
+/**
+ * monitor_tbufs - monitor the contents of tbufs and output to a file
+ * @logfile:       the FILE * representing the file to log to
+ */
+int monitor_tbufs(FILE *logfile)
+{
+    int i, j;
+    void *tbufs_mapped;          /* pointer to where the tbufs are mapped    */
+    struct t_buf **meta;         /* pointers to the trace buffer metadata    */
+    struct t_rec **data;         /* pointers to the trace buffer data areas
+                                  * where they are mapped into user space.   */
+    int *tails;                  /* store tail indexes for the trace buffers */
+    struct t_buf *tbufs_phys;    /* physical address of the tbufs             */
+    
+    /* setup access to trace buffers */
+    tbufs_phys   = get_tbuf_ptrs();
+    tbufs_mapped = map_tbufs(tbufs_phys);
+
+    /* build arrays of convenience ptrs */
+    meta  = init_bufs_ptrs (tbufs_mapped);
+    data  = init_rec_ptrs  (tbufs_phys, tbufs_mapped, meta);
+    tails = init_tail_idxs (meta);
+
+    /* now, scan buffers for events */
+    while(!interrupted) {
+        for(i = 0; i < opts.num_cpus; i++) {
+            signed long newdata = meta[i]->head - tails[i];
+            signed long prewrap = newdata;
+
+           /* correct newdata and prewrap in case of a pointer wrap */
+            if(newdata < 0) {
+                newdata += meta[i]->size;
+                prewrap  = meta[i]->size - tails[i];
+            }
+
+            if(newdata >= opts.new_data_thresh) {
+                /* output pre-wrap data */
+                for(j = 0; j < prewrap; j++)
+                    print_rec(i, data[i] + tails[i] + j, logfile);
+                
+                /* output post-wrap data, if any */                    
+                for(j = 0; j < (newdata - prewrap); j++)
+                    print_rec(i, data[i] + j, logfile);  
+                
+                tails[i] += newdata;
+                if(tails[i] >= meta[i]->size) tails[i] = 0;
+            }
+        }
+        nanosleep(&opts.poll_sleep, NULL);
+    }
+
+    /* cleanup */
+    free(meta);
+    free(data);
+    free(tails);
+    /* don't need to munmap - cleanup is automatic */
+    fclose(logfile);
+
+    return 0;
+}
+
+
+/******************************************************************************
+ * Various declarations / definitions GNU argp needs to do its work
+ *****************************************************************************/
+
+
+/* command parser for GNU argp - see GNU docs for more info */
+error_t cmd_parser(int key, char *arg, struct argp_state *state)
+{
+    settings_t *setup = (settings_t *)state->input;
+
+    switch(key)
+    {
+    case 't': /* set new records threshold for logging */
+    {
+        char *inval;
+        setup->new_data_thresh = strtol(arg, &inval, 0);
+        if(inval == arg) argp_usage(state);
+    }
+    
+    break;
+
+    case 's': /* set sleep time (given in milliseconds) */
+    {
+        char *inval;
+        setup->poll_sleep = millis_to_timespec(strtol(arg, &inval, 0));
+        if(inval == arg) argp_usage(state);
+    }
+    break;
+
+    case 'n': /* set number of CPU trace buffers to map */
+    {
+        char *inval;
+        setup->num_cpus = strtol(arg, &inval, 0);
+        if(inval == arg) argp_usage(state);
+    }
+    break;
+    
+    case ARGP_KEY_ARG:
+        if(state->arg_num == 0)
+            setup->outfile = arg;
+        else
+            argp_usage(state);
+        break;
+        
+    default:
+        return ARGP_ERR_UNKNOWN;
+    }
+
+    return 0;
+}
+
+#define xstr(x) str(x)
+#define str(x) #x
+
+const struct argp_option cmd_opts[] =
+{
+    { .name = "log-thresh", .key='t', .arg="l",
+      .doc =
+      "Set number, l, of new records required to trigger a write to output "
+      "(default " xstr(NEW_DATA_THRESH) ")." },
+
+    { .name = "poll-sleep", .key='s', .arg="p",
+      .doc = 
+      "Set sleep time, p, in milliseconds between polling the trace buffer "
+      "for new data (default " xstr(POLL_SLEEP_MILLIS) ")." },
+
+    { .name = "num-cpus", .key = 'n', .arg="i",
+      .doc = 
+      "Set number, i, of CPU trace buffers to map.  This should not exceed "
+      "the number of CPUs in the system (default " xstr(NUM_CPUS) ")." },
+
+    {0}
+};
+
+const struct argp parser_def =
+{
+    .options = cmd_opts,
+    .parser = cmd_parser,
+    .args_doc = "[output file]",
+    .doc =
+    "Tool to capure Xen trace buffer data"
+    "\v"
+    "This tool is used to capture trace buffer data from Xen.  The data is "
+    "output as space-separated decimal numbers, represented in ASCII, in "
+    "the following order:\n\n"
+    "  CPU TSC EVENT DATA1 DATA2 DATA3 DATA4 DATA5\n"
+};
+
+
+const char *argp_program_version     = "xentrace v1.0";
+const char *argp_program_bug_address = "<mark.a.williamson@intel.com>";
+        
+    
+int main(int argc, char **argv)
+{
+    int ret;
+    FILE *logfile = stdout;
+
+    const struct sigaction act = { .sa_handler = close_handler };
+
+    opts.outfile = 0;
+    opts.num_cpus = 1;
+    opts.poll_sleep = millis_to_timespec(POLL_SLEEP_MILLIS);
+    opts.new_data_thresh = NEW_DATA_THRESH;
+
+    argp_parse(&parser_def, argc, argv, 0, 0, &opts);
+
+    if(opts.outfile) {
+        logfile = fopen(opts.outfile, "w");
+    }
+    
+    /* ensure that if we get a signal, we'll do cleanup, then exit */
+    sigaction(SIGHUP,  &act, 0);
+    sigaction(SIGTERM, &act, 0);
+    sigaction(SIGINT,  &act, 0);
+
+    ret = monitor_tbufs(logfile);
+
+    return ret;
+}
diff --git a/tools/xentrace/xentrace_cpusplit.py b/tools/xentrace/xentrace_cpusplit.py
new file mode 100644 (file)
index 0000000..9847b79
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+
+# by Mark Williamson, (C) 2004 Intel Research Cambridge
+
+# Program for separating trace buffer data into per-cpu log files.
+
+import re, sys, signal
+
+def usage():
+    print >> sys.stderr, \
+          "Usage: " + sys.argv[0] + """ base-name
+          Separates ASCII trace buffer data on stdin into per-CPU trace
+          files.  The trace files are named by appending the CPU number
+          to the base name supplied on the command line.
+
+          Depending on your system and the volume of trace buffer data,
+          this script may not be able to keep up with the output of xentrace
+          if it is piped directly.  In these circumstances you should have
+          xentrace output to a file for processing off-line.
+          """
+    sys.exit(1)
+    
+def sighand(x,y):
+    global interrupted
+    interrupted = 1
+
+signal.signal(signal.SIGTERM, sighand)
+signal.signal(signal.SIGHUP,  sighand)
+signal.signal(signal.SIGINT,  sighand)
+
+r = re.compile("(\d) .*")
+
+if len(sys.argv) < 2:
+    usage()
+else:
+    base_name = sys.argv[1]
+
+files = {}
+interrupted = 0
+
+while not interrupted:
+    try:
+        line = sys.stdin.readline()
+        if not line: break
+        
+        m = r.match(line)
+
+        if not m: print >> sys.stderr, "Invalid input line."
+        
+        cpu = m.group(1)
+        
+        if not files.has_key(base_name + str(cpu)):
+            files[base_name + str(cpu)] = open(base_name + str(cpu), "w")
+            
+        print >> files[base_name + str(cpu)], line,
+
+    except IOError: sys.exit()
+
+# files closed automatically
diff --git a/tools/xentrace/xentrace_format.py b/tools/xentrace/xentrace_format.py
new file mode 100644 (file)
index 0000000..b8f8d01
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+
+# by Mark Williamson, (C) 2004 Intel Research Cambridge
+
+# Program for reformatting trace buffer output according to user-supplied rules
+
+import re, sys, string, signal
+
+def usage():
+    print >> sys.stderr, \
+          "Usage: " + sys.argv[0] + """ defs-file
+          Parses trace data in ASCII format and reformats it according to the
+          rules in a file of definitions.  The rules in this file should have
+          the format ({ and } show grouping and are not part of the syntax):
+
+          {event_id}{whitespace}{text format string}
+
+          The textual format string may include the format specifiers:
+            %(cpu)s, %(tsc), %(event)s, %(1)s, %(2)s, %(3)s, %(4)s, %(5)s
+
+          Which correspond to the CPU number, event ID, timestamp counter and
+          the 5 data fields from the trace record.  There should be one such
+          rule for each type of event.
+          
+          Depending on your system and the volume of trace buffer data,
+          this script may not be able to keep up with the output of xentrace
+          if it is piped directly.  In these circumstances you should have
+          xentrace output to a file for processing off-line.
+          """
+    sys.exit(1)
+
+def read_defs(defs_file):
+    defs = {}
+    
+    fd = open(defs_file)
+
+    reg = re.compile('(\d+)\s+(\S.*)')
+
+    while True:
+        line = fd.readline()
+        if not line:
+            break
+        
+        m = reg.match(line)
+
+        if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1)
+        
+        defs[m.group(1)] = m.group(2)
+
+    return defs
+
+def sighand(x,y):
+    global interrupted
+    interrupted = 1
+
+##### Main code
+
+if len(sys.argv) < 2:
+    usage()
+
+signal.signal(signal.SIGTERM, sighand)
+signal.signal(signal.SIGHUP,  sighand)
+signal.signal(signal.SIGINT,  sighand)
+
+interrupted = 0
+
+defs = read_defs(sys.argv[1])
+
+reg = re.compile('(\d+) (\d+) (\d+) (.*)')
+
+while not interrupted:
+    try:
+        line = sys.stdin.readline()
+        if not line:
+            break
+
+        m = reg.match(line)
+
+        if not m: print >> sys.stderr, "Invalid input line."
+
+        s = string.split(m.group(4))
+
+        args = {'cpu'   : m.group(1),
+                'tsc'   : m.group(2),
+                'event' : m.group(3) }
+
+        i = 0
+        for item in s:
+            args[str(i)] = item
+            i += 1
+
+        if defs.has_key(m.group(3)): print defs[m.group(3)] % args
+        # silently skip lines we don't have a format for - a 'complain' option
+        # should be added if needed
+
+    except IOError: sys.exit()
index ac2df96139f868d1cf98964780a0b2fda0cee279..2e5c315530e50f549b569e11c03c0b908adba9fd 100644 (file)
@@ -14,6 +14,7 @@
 #include <asm/desc.h>
 #include <asm/domain_page.h>
 #include <asm/pdb.h>
+#include <xeno/trace.h>
 
 struct cpuinfo_x86 boot_cpu_data = { 0 };
 /* Lots of nice things, since we only target PPro+. */
@@ -449,4 +450,8 @@ void __init start_of_day(void)
 #endif
 
     watchdog_on = 1;
+
+#ifdef TRACE_BUFFER
+    init_trace_bufs(); /* initialise trace buffers */
+#endif
 }
index 79f4ecc28571dbac088c544c031e8edcd7c1ad02..c225dffd3f2f1ce6d466e7eb4b65e26bdbc86b4e 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/domain_page.h>
 #include <asm/msr.h>
 #include <asm/pdb.h>
+#include <xeno/trace.h>
 
 extern unsigned int alloc_new_dom_mem(struct task_struct *, unsigned int);
 
@@ -431,6 +432,16 @@ long do_dom0_op(dom0_op_t *u_dom0_op)
         ret = 0;
     }
     break;
+
+#ifdef TRACE_BUFFER
+    case DOM0_GETTBUFS:
+    {
+       op->u.gettbufs.phys_addr = get_tb_ptr();
+       copy_to_user(u_dom0_op, op, sizeof(*op));
+       ret = 0;
+    }
+    break;
+#endif
     
     case DOM0_READCONSOLE:
     {
diff --git a/xen/common/trace.c b/xen/common/trace.c
new file mode 100644 (file)
index 0000000..6e741b7
--- /dev/null
@@ -0,0 +1,103 @@
+/******************************************************************************
+ *
+ * common/trace.c
+ *
+ * Xen Trace Buffer
+ *
+ * Copyright (C) 2004 by Intel Research Cambridge
+ *
+ * Author: Mark Williamson, mark.a.williamson@intel.com
+ * Date:   January 2004
+ *
+ * The trace buffer code is designed to allow debugging traces of Xen to be
+ * generated on UP / SMP machines.  Each trace entry is timestamped so that
+ * it's possible to reconstruct a chronological record of trace events.
+ *
+ * See also include/xeno/trace.h and the dom0 op in
+ * include/hypervisor-ifs/dom0_ops.h
+ *
+ *****************************************************************************/
+
+#include <xeno/config.h>
+
+#ifdef TRACE_BUFFER /* don't compile this stuff in unless explicitly enabled */
+
+#include <asm/timex.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <xeno/lib.h>
+#include <xeno/sched.h>
+#include <xeno/slab.h>
+#include <xeno/smp.h>
+#include <xeno/spinlock.h>
+#include <xeno/trace.h>
+#include <asm/atomic.h>
+
+
+
+/* Pointers to the meta-data objects for all system trace buffers */
+struct t_buf *t_bufs[NR_CPUS];
+
+/* a flag recording whether initialisation has been done */
+atomic_t tb_init_done = ATOMIC_INIT(0);
+
+
+/**
+ * init_trace_bufs - performs initialisation of the per-cpu trace buffers.
+ *
+ * This function is called at start of day in order to initialise the per-cpu
+ * trace buffers.  The trace buffers are then available for debugging use, via
+ * the %TRACE_xD macros exported in <xeno/trace.h>.
+ */
+void init_trace_bufs()
+{
+    int i;
+    void *d;     /* trace buffer area pointer */
+
+    d = kmalloc(smp_num_cpus * TB_SIZE, GFP_KERNEL);
+    
+    if( d == NULL ) {
+        printk("Xen trace buffers: memory allocation failed\n");
+        return;
+    }
+    
+    for(i = 0; i < smp_num_cpus; i++) {
+        struct t_buf *buf = t_bufs[i]
+         = (struct t_buf *)( (unsigned int)d + TB_SIZE * i );
+        
+        /* for use in Xen */
+        buf->vdata = (struct t_rec *)
+            ( (unsigned int)buf + sizeof(struct t_buf) );
+        buf->head_ptr = buf->vdata;
+       spin_lock_init(&buf->lock);
+        
+        /* for use in user space */
+        buf->data = (struct t_rec *)__pa(buf->vdata);
+       buf->head = 0;
+
+        /* for use in both */
+       buf->size = (TB_SIZE - sizeof(struct t_buf)) / sizeof(struct t_rec);
+    }
+
+    printk("Xen trace buffers: initialised\n");
+    wmb(); /* above must be visible before tb_init_done flag set */
+
+    atomic_set(&tb_init_done, 1);
+}
+
+    
+
+/**
+ * get_tb_ptr - return physical address of the trace buffers.
+ *
+ * Called by the %DOM0_GETTBUFS dom0 op to fetch the physical address of the
+ * trace buffers.
+ */
+struct t_buf *get_tb_ptr()
+{
+    /* a physical address (user space maps this using /dev/mem) */
+  return (struct t_buf *)__pa(t_bufs[0]);
+}
+
+#endif /* #ifdef TRACE_BUFFER */
index 4562aa1c49b68dd0087ec2e90c66bf212ed5c3be..aeb1f519170d7adc92e07afff972b70aea07eb1c 100644 (file)
@@ -17,7 +17,7 @@
  * This makes sure that old versions of dom0 tools will stop working in a
  * well-defined way (rather than crashing the machine, for instance).
  */
-#define DOM0_INTERFACE_VERSION   0xAAAA0005
+#define DOM0_INTERFACE_VERSION   0xAAAA0006
 
 
 /*
@@ -210,6 +210,14 @@ typedef struct dom0_pincpudomain_st
     int          cpu;  /* -1 implies unpin */
 } dom0_pincpudomain_t;
 
+/* Get trace buffers physical base pointer */
+#define DOM0_GETTBUFS         21
+typedef struct dom0_gettbufs_st
+{
+  /* OUT variable - location of the trace buffers */
+  struct t_buf *phys_addr;
+} dom0_gettbufs_t;
+
 typedef struct dom0_op_st
 {
     unsigned long cmd;
@@ -232,6 +240,7 @@ typedef struct dom0_op_st
        dom0_settime_t          settime;
        dom0_readconsole_t      readconsole;
        dom0_pincpudomain_t     pincpudomain;
+        dom0_gettbufs_t         gettbufs;
     } u;
 } dom0_op_t;
 
index 35cdd22ee4b9b6d3c7335afb2b1fc5572b4ab00c..e8dffa1a2ae40271340077b8bee55750862f3983 100644 (file)
 #define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \
                            __FILE__ , __LINE__ , ## _a )
 #define STACK_GUARD
+#define TRACE_BUFFER
 #else
 #define DPRINTK(_f, _a...) ((void)0)
 #endif
diff --git a/xen/include/xeno/trace.h b/xen/include/xeno/trace.h
new file mode 100644 (file)
index 0000000..c6a1a84
--- /dev/null
@@ -0,0 +1,179 @@
+/******************************************************************************
+ *
+ * include/xeno/trace.h
+ *
+ * Xen Trace Buffer
+ *
+ * Copyright (C) 2003 by Intel Research Cambridge
+ *
+ * Author: Mark Williamson, mark.a.williamson@intel.com
+ * Date:   January 2004
+ *
+ * The trace buffer code is designed to allow debugging traces of Xen to be
+ * generated on UP / SMP machines.  Each trace entry is timestamped so that
+ * it's possible to reconstruct a chronological record of trace events.
+ *
+ * Access to the trace buffers is via a dom0 hypervisor op and analysis of
+ * trace buffer contents can then be performed using a userland tool.
+ *
+ * See also common/trace.c and the dom0 op in include/hypervisor-ifs/dom0_ops.h
+ *
+ *****************************************************************************/
+
+#ifdef TRACE_BUFFER
+
+#ifndef __XENO_TRACE_H__
+#define __XENO_TRACE_H__
+
+#ifdef __KERNEL__
+
+#include <xeno/spinlock.h>
+#include <asm/page.h>
+#include <xeno/types.h>
+#include <xeno/sched.h>
+#include <asm/atomic.h>
+#include <asm/current.h>
+#include <asm/msr.h>
+
+#endif /* #ifdef __KERNEL__ */
+
+/******************************************************************************
+ * Data structure declarations
+ *****************************************************************************/
+
+/* This structure represents a single trace buffer record. */
+struct t_rec {
+    u64 cycles;               /* 64 bit cycle counter timestamp */
+    u32 event;                /* 32 bit event ID                */
+    u32 d1, d2, d3, d4, d5;   /* event data items               */
+};
+
+/* This structure contains the metadata for a single trace buffer.  The head
+ * field, indexes into an array of struct t_rec's.
+ */
+struct t_buf {
+    struct t_rec *data;     /* pointer to data area.  physical address
+                            * for convenience in user space code            */
+
+    unsigned int size;      /* size of the data area, in t_recs              */
+    unsigned int head;      /* array index of the most recent record         */
+
+#ifdef __KERNEL__
+    struct t_rec *head_ptr; /* pointer to the head record                    */
+    struct t_rec *vdata;    /* virtual address pointer to data,
+                             * for use in Xen */
+    spinlock_t lock;        /* ensure mutually exlusive access (for inserts) */
+#endif /* #ifdef __KERNEL__ */
+
+    /* never add anything here - the kernel stuff must be the last elements */
+};
+
+/******************************************************************************
+ * Functions
+ *****************************************************************************/
+
+#ifdef __KERNEL__
+
+/* Used to initialise trace buffer functionality */
+void init_trace_bufs();
+
+/* used to retrieve the physical address of the trace buffers */
+struct t_buf *get_tb_ptr();
+
+/**
+ * trace - Enters a trace tuple into the trace buffer for the current CPU.
+ * @event: the event type being logged
+ * @d1...d5: the data items for the event being logged
+ *
+ * Logs a trace record into the appropriate buffer.  Returns nonzero on
+ * failure, otherwise 0.  Failure occurs only if the trace buffers are not yet
+ * initialised.
+ */
+static inline int trace(u32 event, u32 d1, u32 d2, u32 d3, u32 d4, u32 d5)
+{
+    extern struct t_buf *t_bufs[];      /* global array of pointers to bufs */
+    extern atomic_t tb_init_done;       /* set when buffers are initialised */
+    unsigned long flags;                /* for saving interrupt flags       */
+    struct t_buf *buf;                  /* the buffer we're working on      */
+    struct t_rec *rec;                  /* next record to fill out          */
+
+
+    if(!atomic_read(&tb_init_done)) return -1;
+
+    buf = t_bufs[smp_processor_id()];
+    rec = buf->head_ptr;
+
+    spin_lock_irqsave(&buf->lock, flags);
+    /* interrupts _disabled locally_ during the following code */
+
+    rdtscll(rec->cycles);
+    rec->event = event;
+    rec->d1 = d1;
+    rec->d2 = d2;
+    rec->d3 = d3;
+    rec->d4 = d4;
+    rec->d5 = d5;
+
+    wmb(); /* above must be visible before reader sees index updated */
+
+    if( likely( buf->head_ptr < ( buf->vdata + buf->size - 1) ) ) {
+        buf->head_ptr++;
+        buf->head++;
+    } else {
+       buf->head = 0;
+        buf->head_ptr = buf->vdata;
+    }
+
+    spin_unlock_irqrestore(&buf->lock, flags);
+    /* Interrupts now _re-enabled locally_ */
+    
+    return 0;
+}
+
+
+#endif /* #ifdef __KERNEL__ */
+
+
+/******************************************************************************
+ * Macros
+ *****************************************************************************/
+
+/* How much space is allowed for a single trace buffer, including data and
+ * metadata (and maybe some waste).
+ */
+#define TB_SIZE PAGE_SIZE
+
+#ifdef __KERNEL__
+
+/* avoids troubling the caller with casting their arguments to a trace macro */
+#define trace_do_casts(e,d1,d2,d3,d4,d5)  \
+                 trace(e,                 \
+                      (unsigned long)d1, \
+                      (unsigned long)d2, \
+                      (unsigned long)d3, \
+                      (unsigned long)d4, \
+                      (unsigned long)d5)
+
+/* convenience macros for calling the trace function */
+#define TRACE_0D(event)                trace_do_casts(event,0, 0, 0, 0, 0 )
+#define TRACE_1D(event,d)              trace_do_casts(event,d, 0, 0, 0, 0 )
+#define TRACE_2D(event,d1,d2)          trace_do_casts(event,d1,d2,0, 0, 0 )
+#define TRACE_3D(event,d1,d2,d3)       trace_do_casts(event,d1,d2,d3,0, 0 )
+#define TRACE_4D(event,d1,d2,d3,d4)    trace_do_casts(event,d1,d2,d3,d4,0 )
+#define TRACE_5D(event,d1,d2,d3,d4,d5) trace_do_casts(event,d1,d2,d3,d4,d5)
+
+#endif /* #ifdef __KERNEL__        */
+
+#endif /* #ifndef __XENO_TRACE_H__ */
+
+#else  /* #ifdef TRACE_BUFFER      */
+
+/* define out macros so that they can be left in code when tracing is disabled */
+#define TRACE_0D(event)                ((void)0)
+#define TRACE_1D(event,d)              ((void)0)
+#define TRACE_2D(event,d1,d2)          ((void)0)
+#define TRACE_3D(event,d1,d2,d3)       ((void)0)
+#define TRACE_4D(event,d1,d2,d3,d4)    ((void)0)
+#define TRACE_5D(event,d1,d2,d3,d4,d5) ((void)0)
+
+#endif /* #ifdef TRACE_BUFFER      */